package client

import (
	"context"
	"crypto/tls"
	"errors"
	"gitee.com/yangzx6606/core/pattern"
	log "github.com/golang/glog"
	"github.com/quic-go/quic-go"
	"time"
)

// QuicClient QUIC协议客户端封装
type QuicClient struct {
	pattern.AbstractSubject[*Packet]
	address     string
	name        string
	isConnected bool
	isOpened    bool

	conn   quic.Connection
	stream quic.Stream
}

func NewQuicClient(address string, name string) *QuicClient {
	if len(name) == 0 {
		name = "quic-basic-client"
	}
	return &QuicClient{name: name, address: address}
}

func (c *QuicClient) Open() error {
	if c.isOpened {
		return errors.New("已打开 ")
	}
	c.isOpened = true
	go c.doWork()
	return nil
}

func (c *QuicClient) Write(p *Packet) error {
	if c.isConnected {
		_, err := c.stream.Write(p.Data)
		return err
	}
	return errors.New("未连接")
}

func (c *QuicClient) Close() {
	if c.isOpened {
		c.isOpened = false
		_ = c.conn.CloseWithError(quic.ApplicationErrorCode(0), "close")
		_ = c.stream.Close()
	}
}

func (c *QuicClient) doWork() {
	buffer := make([]byte, 1024*128)
	for c.isOpened {
		if c.tryConnected() != nil {
			if c.isOpened {
				time.Sleep(time.Millisecond * 3000)
			}
			continue
		}
		n, err := c.stream.Read(buffer)
		if err != nil {
			if c.isOpened {
				_ = c.stream.Close()
				c.isConnected = false
				c.Notify(NewPacket(PacketTypeConnectLost, nil))
				// 连接断开后，等待3秒后再连接
				time.Sleep(time.Millisecond * 3000)
			}
			continue
		}
		c.Notify(NewPacket(PacketTypeData, buffer[:n]))
	}
}

func (c *QuicClient) tryConnected() error {
	if c.isConnected {
		return nil
	}
	cfg := &tls.Config{
		InsecureSkipVerify: true,
		NextProtos:         []string{c.name},
	}
	var err error
	c.conn, err = quic.DialAddr(c.address, cfg, nil)
	if err != nil {
		log.Error("连接服务器失败：", err)
		return err
	}
	c.stream, err = c.conn.OpenStreamSync(context.Background())
	if err != nil {
		return err
	}
	c.isConnected = true
	c.Notify(NewPacket(PacketTypeConnected, nil))
	return nil
}
